﻿/// <reference path="jquery-1.7.2.js" />
/// <reference path="knockout-2.1.0.debug.js" />

jQuery.support.cors = true; // force cross-site scripting (as of jQuery 1.5)

String.prototype.format = function () {
    var pattern = /\{\d+\}/g,
        placeHolder = /\d+/,
        args = arguments;
    return this.replace(pattern, function (capture) { return args[capture.match(placeHolder)]; });
};

var APP = (function () {

    String.prototype.contains = function (it) { return this.indexOf(it) != -1; };

    var mapIcon = function (skycode) {
        switch (+skycode) {
            case 1: case 2: case 3: case 4: case 37: case 47:
                return "images/weather/thunderstorm.gif";
            case 38:
                return "images/weather/chance_of_storm.gif";
            case 29: case 30: case 33:
                return "images/weather/partly_cloudy.gif";
            case 26: case 27:
                return "images/weather/cloudy.gif";
            case 28:
                return "images/weather/mostly_cloudy.gif";
            case 35: case 39: case 45: case 46:
                return "images/weather/showers.gif";
            case 19: case 20: case 21: case 22:
                return "images/weather/fog.gif";
            case 5: case 13: case 14: case 15: case 16: case 18: case 25: case 41: case 42: case 43:
                return "images/weather/snow.gif";
            case 31: case 32: case 36:
                return "images/weather/sunny.gif";
            case 34:
                return "images/weather/mostly_sunny.gif";
            case 23: case 24:
                return "images/weather/windy.gif";
            case 9: case 10: case 12: case 40:
                return "images/weather/rain.gif";
            case 11:
                return "images/weather/showers.gif";
            case 6: case 7: case 8: case 17: // hail
                return "images/weather/sleet.gif";
            default:
                return "images/weather/na.gif";
        }
    };

    var model = {
        location: "",
        city: ko.observable("loading..."),
        condition: ko.observable(),
        temperature: ko.observable(),
        humidity: ko.observable(),
        wind: ko.observable(),
        celsius: ko.observable(false),
        feelslike: "",
        observationtime: "",
        url: "",
        alertsUrl: "",
        time24: false,

        forecast0_day: ko.observable(),
        forecast0_low: ko.observable(),
        forecast0_high: ko.observable(),
        forecast0_icon: ko.observable(),
        forecast0_condition: ko.observable(),

        forecast1_day: ko.observable(),
        forecast1_low: ko.observable(),
        forecast1_high: ko.observable(),
        forecast1_icon: ko.observable(),
        forecast1_condition: ko.observable(),

        forecast2_day: ko.observable(),
        forecast2_low: ko.observable(),
        forecast2_high: ko.observable(),
        forecast2_icon: ko.observable(),
        forecast2_condition: ko.observable(),

        forecast3_day: ko.observable(),
        forecast3_low: ko.observable(),
        forecast3_high: ko.observable(),
        forecast3_icon: ko.observable(),
        forecast3_condition: ko.observable()
    };

    var settingsClosed = function (event) {
        if (event.closeAction == event.Action.commit) {
            model.location = window.System.Gadget.Settings.readString("location");
            model.celsius(window.System.Gadget.Settings.readString("celsius") == "true");
            model.custom_title = window.System.Gadget.Settings.readString("custom_title");
            model.weather_page = window.System.Gadget.Settings.readString("wp");
            model.time24 = (window.System.Gadget.Settings.readString("time24") == "true") ? true : false;
            getWeather();
        }
    };

    var formatTime = function (time) {
        if (!time) { return ""; }
        var parts = time.split(":");
        var hours = +parts[0];
        var minutes = parts[1];
        if (model.time24) { return "{0}:{1}".format(hours, minutes); }
        return "{0}:{1}{2}".format((hours > 12) ? hours - 12 : hours, minutes, (hours < 12) ? "a" : "p");
    };

    var updateModel = function (data) {
        try {
            var weather = $(data).find("weather");
            model.city(model.custom_title || weather.attr("weatherlocationname"));
            model.url = weather.attr("url");
            model.alertsUrl = model.url.replace("weather.aspx", "bulletin.aspx");
            model.zipcode = weather.attr("zipcode");

            var current = weather.find("current");
            model.condition(current.attr("skytext"));
            model.temperature(current.attr("temperature"));
            model.humidity(current.attr("humidity"));
            model.wind(current.attr("winddisplay"));
            model.feelslike = current.attr("feelslike");
            var time = current.attr("observationtime");
            model.observationtime = formatTime(time);

            function updateForecast(forecast, day, low, high, icon, condition) {
                var f = $(forecast);
                day(f.attr("shortday"));
                low(f.attr("low"));
                high(f.attr("high"));
                icon(mapIcon(f.attr("skycodeday")));
                var precip = parseInt(f.attr("precip"));
                condition(f.attr("skytextday").replace("Thunderstorms", "T-storms") +
                    (precip ? " {0}%".format(precip) : ""));
            }

            var forecasts = weather.find("forecast");
            updateForecast(forecasts[0], model.forecast0_day, model.forecast0_low, model.forecast0_high, model.forecast0_icon, model.forecast0_condition);
            updateForecast(forecasts[1], model.forecast1_day, model.forecast1_low, model.forecast1_high, model.forecast1_icon, model.forecast1_condition);
            updateForecast(forecasts[2], model.forecast2_day, model.forecast2_low, model.forecast2_high, model.forecast2_icon, model.forecast2_condition);
            updateForecast(forecasts[3], model.forecast3_day, model.forecast3_low, model.forecast3_high, model.forecast3_icon, model.forecast3_condition);

            if (weather.attr("alert")) {
                getAlerts();
            }
            else {
                model.alerts = [];
                $("#alerts, #nextformat").hide();
                $("#humidity").show();
            }
        }

        catch (e) {
            model.city("Offline");
        }
    };

    var getBackgroundColor = function () {
        return "#" + (window.System.Gadget.Settings.readString("background") || "333");
    };

    var getForegroundColor = function () {
        return "#" + (window.System.Gadget.Settings.readString("foreground") || "ddd");
    };

    var showColorsFlyout = function () {
        if (window.System.Gadget.Flyout.show) {
            window.System.Gadget.Flyout.show = false;
            return;
        }
        window.System.Gadget.Flyout.file = "colors.html";
        window.System.Gadget.Flyout.onShow = null;
        window.System.Gadget.Flyout.show = true;
    };

    (function () {
        var alertIndex = 0;
        model.alerts = [];
        setInterval(onUpdate, 5000);

        return function onUpdate() {
            var alerts = $("#alerts");
            var humidity = $("#humidity");
            if (model.alerts.length === 0) {
                alerts.hide();
                model.humidity() && humidity.show();
                return;
            }
            alertIndex = (alertIndex + 1) % model.alerts.length;
            if (model.alerts.length > 0) {
                humidity.hide();
                alerts.text(model.alerts[alertIndex].title).show();
            }
        };
    })();

    var connectionFinder = function () {
        model.city("Offline");
        $.ajax({
            url: "http://www.google.com",
            dataType: "html",
            success: function () { getWeather(); },
            error: function () { setTimeout(connectionFinder, 10000); },
            crossDomain: true
        });
    };

    var getWeather = function () {
        $.ajax({
            url: "http://weather.service.msn.com/data.aspx?src=vista&weadegreetype={0}&culture=en-US&wealocations={1}"
                .format(model.celsius() ? "C" : "F", model.location),
            dataType: "xml",
            success: function (data) { updateModel(data); },
            error: function () { connectionFinder(); },
            crossDomain: true
        });
    };

    var findAlerts = function (html) {
        model.alerts = [];
        var h = $(html);
        var alerts = h.find(".alert");
        if (alerts.length > 0) {
            for (var i = 0; i < alerts.length; ++i) {
                var alert = { title: alerts[i].childNodes[0].nodeValue };
                model.alerts.push(alert);
            };
        }
    };

    var getAlerts = function () {
        $.ajax({
            url: model.alertsUrl,
            dataType: "html",
            success: function (data) { findAlerts(data); },
            crossDomain: true
        });
    };

    var openPage = function (url) {
        var shellRun = function (cmd) {
            var shell = new ActiveXObject("WScript.Shell");
            shell.Run(cmd);
        };
        shellRun(url);
    };

    var openWeatherPage = function () {
        openPage(model.weather_page || model.url);
    };

    var openAlertsPage = function () {
        openPage(model.alertsUrl);
    };

    var nextFormat = function () {
        var current = 0;
        var formats = ["#format0", "#format2", "#format1"];
        var heights = [173, 200, 77];

        return function (format) {
            current = (format !== undefined) ? format : ((current + 1) % formats.length);
            window.System.Gadget.Settings.writeString("format", current);
            $.each(formats, function (i, v) { $(v).hide(); });
            $(formats[current]).show();
            $(document.body).height(heights[current]);
        };
    }();

    var tooltip = function () {
        var id = 'tt';
        var maxw = 300;
        var speed = 10;
        var timer = 20;
        var endalpha = 95;
        var alpha = 0;
        var tt, c, h;
        return {
            show: function (v, w) {
                if (tt == null) {
                    tt = document.createElement('div');
                    tt.setAttribute('id', id);
                    c = document.createElement('div');
                    c.setAttribute('id', id + 'cont');
                    tt.appendChild(c);
                    document.body.appendChild(tt);
                    tt.style.opacity = 0;
                    tt.style.filter = 'alpha(opacity=0)';
                    document.onmousemove = this.pos;
                }
                tt.style.left = 0;
                tt.style.display = 'block';
                c.innerHTML = v;
                tt.style.width = w ? w + 'px' : 'auto';
                if (!w) { tt.style.width = tt.offsetWidth; }
                if (tt.offsetWidth > maxw) { tt.style.width = maxw + 'px'; }
                h = parseInt(tt.offsetHeight) + 5;
                clearInterval(tt.timer);
                tt.timer = setInterval(function () { tooltip.fade(1); }, timer);
            },
            pos: function () {
                var u = event.clientY + document.documentElement.scrollTop;
                tt.style.top = Math.max(2, (u - h)) + 'px';
            },
            fade: function (d) {
                var a = alpha;
                if ((a != endalpha && d == 1) || (a != 0 && d == -1)) {
                    var i = speed;
                    if (endalpha - a < speed && d == 1) {
                        i = endalpha - a;
                    } else if (alpha < speed && d == -1) {
                        i = a;
                    }
                    alpha = a + (i * d);
                    tt.style.opacity = alpha * .01;
                    tt.style.filter = 'alpha(opacity=' + alpha + ')';
                } else {
                    clearInterval(tt.timer);
                    if (d == -1) { tt.style.display = 'none'; }
                }
            },
            hide: function () {
                clearInterval(tt.timer);
                tt.timer = setInterval(function () { tooltip.fade(-1); }, timer);
            }
        };
    }();

    var feelsLike = function () {
        model.feelslike && tooltip.show("Feels like {0}&deg;, {1}"
            .format(model.feelslike, model.observationtime));
    };

    var setBackgroundImage = function (imagePath) {
        if (imagePath == null) {
            imagePath = window.System.Gadget.Settings.readString("backgroundImage");
        }
        if (imagePath) {
            window.System.Gadget.Settings.writeString("backgroundImage", imagePath);
            $(document.body).css("background-image", "url('{0}')".format(imagePath));
        }
    };

    return {
        getCity: function () {
            return model.city();
        },

        removeBackgroundImage: function () {
            window.System.Gadget.Settings.writeString("backgroundImage", "");
            $(document.body).css("background-image", "");
        },

        initialize: function () {
            var format = window.System.Gadget.Settings.readString("format") || "0";
            nextFormat(+format);
            model.location = window.System.Gadget.Settings.readString("location") || "48176";
            window.System.Gadget.Settings.writeString("location", model.location);
            model.celsius(window.System.Gadget.Settings.readString("celsius") == "true");
            model.custom_title = window.System.Gadget.Settings.readString("custom_title");
            model.weather_page = window.System.Gadget.Settings.readString("wp");
            model.time24 = (window.System.Gadget.Settings.readString("time24") == "true") ? true : false;
            $(document.body).css("color", getForegroundColor());
            $(document.body).css("background-color", getBackgroundColor());
            setBackgroundImage();

            window.System.Gadget.onSettingsClosed = settingsClosed;
            window.System.Gadget.settingsUI = "settings.html";
            $("#city").bind("mousedown", openWeatherPage);
            $("#alerts").bind("mousedown", openAlertsPage);
            $("td img").bind("mouseenter", function () { tooltip.show($(this).attr("tip")); });
            $("td img").bind("mouseleave", tooltip.hide);
            $("#temperature").bind("mouseenter", feelsLike);
            $("#temperature").bind("mouseleave", tooltip.hide);
            $("#nextformat").bind("mousedown", function () { nextFormat(); });
            $("#colorsimg").bind("mousedown", showColorsFlyout);
            $("#container").bind("mouseenter", function () {
                $("#colorsimg, #nextformat").stop(false, true).fadeIn();
            });
            $("#container").bind("mouseleave", function () {
                $("#colorsimg, #nextformat").stop(false, true).fadeOut();
            });

            getWeather();
            setInterval(getWeather, 10 * 60000);
            ko.applyBindings(model);

            var dropTarget = $("#container");
            dropTarget.bind("dragenter dragover", function () { return false; });
            dropTarget.bind("drop", function () {
                var file = window.System.Shell.itemFromFileDrop(window.event.dataTransfer, 0);
                file && file.path && setBackgroundImage(file.path);
            });
        }
    };
})();

$(APP.initialize)